#include "ItemDelegate.h"



#include <ItemGroup.h>
#include <ItemReference.h>
#include <ItemModel.h>



#include <QAbstractProxyModel>
#include <QComboBox>
#include <QLineEdit>
#include <QPlainTextEdit>
#include <QDebug>



ItemDelegate::ItemDelegate(QObject *parent) :
    QStyledItemDelegate(parent)
{
}



QWidget* ItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &viewIndex) const
{
    Q_UNUSED(option);

    if (!viewIndex.isValid()) return 0;

    const QAbstractProxyModel* proxy = dynamic_cast<const QAbstractProxyModel*>(viewIndex.model());

    QModelIndex index = viewIndex;
    if (proxy)
    {
        index = proxy->mapToSource(viewIndex);
    }


    switch (index.column())
    {
    case AbstractItem::IdData:
    case AbstractItem::CategoryData:
        return 0;
    }


    QModelIndex categoryIndex = index.sibling(index.row(), AbstractItem::CategoryData);
    if (!categoryIndex.isValid()) return 0;

    bool ok = false;
    int category = categoryIndex.data(Qt::EditRole).toInt(&ok);
    if (!ok) return 0;

    switch (category)
    {
    case ItemCategory_Root: return 0;
    case ItemCategory_Group: return groupEditor(parent, index);
    case ItemCategory_Item: return itemEditor(parent, index);
    case ItemCategory_Reference: return referenceEditor(parent, index);
    default: return 0;
    }
}



void ItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    if (!index.isValid()) return;

    QLineEdit* le = dynamic_cast<QLineEdit*>(editor);
    if (le)
    {
        le->setText(index.data(Qt::EditRole).toString());
        le->selectAll();
        return;
    }

    QPlainTextEdit* pe = dynamic_cast<QPlainTextEdit*>(editor);
    if (pe)
    {
        pe->setPlainText(index.data(Qt::EditRole).toString());
        return;
    }

    QComboBox* cb = dynamic_cast<QComboBox*>(editor);
    if (cb)
    {
        populateCombo(cb, index);
        return;
    }
}



void ItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    //qDebug() << Q_FUNC_INFO;
    QLineEdit* le = dynamic_cast<QLineEdit*>(editor);
    if (le)
    {
        model->setData(index, le->text(), Qt::EditRole);
        return;
    }


    QPlainTextEdit* pe = dynamic_cast<QPlainTextEdit*>(editor);
    if (pe)
    {
        model->setData(index, pe->toPlainText());
        return;
    }

    QComboBox* cb = dynamic_cast<QComboBox*>(editor);
    if (cb)
    {
        model->setData(index, cb->itemData(cb->currentIndex()));
        return;
    }
}



void ItemDelegate::setEditorFilter(const int &field, const QString &filterId, const int &filter)
{
    FilterData filterData;
    filterData.field = field;
    filterData.filterId = filterId;
    filterData.filter = filter;

    if (m_filters.contains(filterData)) return;

    m_filters.append(filterData);
}



QWidget* ItemDelegate::groupEditor(QWidget *parent, const QModelIndex &index) const
{
    if (index.column() != AbstractItem::NameData) return 0;

    return new QLineEdit(parent);
}



QWidget* ItemDelegate::itemEditor(QWidget *parent, const QModelIndex &index) const
{
    if (index.column() != Item::NameData && index.column() != Item::ValueData) return 0;

    return new QLineEdit(parent);
}



QWidget* ItemDelegate::referenceEditor(QWidget *parent, const QModelIndex &index) const
{
    switch (index.column())
    {
    case ItemReference::NameData:
    case ItemReference::ValueData:
        return new QLineEdit(parent);
    case ItemReference::RefData:
    case ItemReference::TypeData:
        return new QComboBox(parent);
    }

    return 0;
}



void ItemDelegate::populateCombo(QComboBox* combo, const QModelIndex& viewIndex) const
{
    if (!combo || !viewIndex.isValid()) return;
    QModelIndex index = viewIndex;

    const QAbstractProxyModel* proxy = dynamic_cast<const QAbstractProxyModel*>(viewIndex.model());
    if (proxy)
    {
        index = proxy->mapToSource(viewIndex);
    }

    // create type data combo
    if (index.column() == ItemReference::TypeData)
    {
        for (int type = ItemReference::ReferenceType_FIRST; type <= ItemReference::ReferenceType_LAST; type++)
        {
            combo->addItem(ItemReference::typeName(type), type);
        }
        combo->setCurrentIndex(index.data(Qt::EditRole).toInt());
        return;
    }


    QModelIndex typeIndex = index.sibling(index.row(), ItemReference::TypeData);
    if (!typeIndex.isValid()) return;

    const ItemModel* model = dynamic_cast<const ItemModel*>(index.model());
    if (!model) return;


    QModelIndex groupIndex = model->index(0, AbstractItem::IdData, QModelIndex());
    while (groupIndex.isValid())
    {
        if (isFiltered(viewIndex.column(), groupIndex))
        {
            groupIndex = groupIndex.sibling(groupIndex.row()+1, AbstractItem::IdData);
            continue;
        }

        if (typeIndex.data(Qt::EditRole).toInt() == ItemReference::GroupRef)
        {
            addToCombo(combo, groupIndex);
            groupIndex = groupIndex.sibling(groupIndex.row()+1, AbstractItem::IdData);
            continue;
        }

        QModelIndex itemIndex = groupIndex.child(0, AbstractItem::IdData);
        while (itemIndex.isValid())
        {
            addToCombo(combo, itemIndex);
            itemIndex = itemIndex.sibling(itemIndex.row()+1, AbstractItem::IdData);
        }

        groupIndex = groupIndex.sibling(groupIndex.row()+1, AbstractItem::IdData);
    }
}



void ItemDelegate::addToCombo(QComboBox* combo, const QModelIndex& optionIndex) const
{
    if (!optionIndex.isValid()) return;

    QModelIndex nameIndex = optionIndex.sibling(optionIndex.row(), AbstractItem::NameData);
    QModelIndex idIndex = optionIndex.sibling(optionIndex.row(), AbstractItem::IdData);

    if (!nameIndex.isValid() || !idIndex.isValid()) return;

    combo->addItem(nameIndex.data(Qt::DisplayRole).toString(), idIndex.data(Qt::EditRole));
}



bool ItemDelegate::isFiltered(int field, const QModelIndex &index) const
{
    QModelIndex idIndex = index.sibling(index.row(), AbstractItem::IdData);
    if (!idIndex.isValid()) return false;

    foreach (const FilterData filterData, m_filters)
    {
        if (filterData.field != field) continue;

        switch (filterData.filter)
        {
        case IncludeFilter:
            return !idIndex.data(Qt::EditRole).toString().contains(filterData.filterId);

        case ExcludeFilter:
            return idIndex.data(Qt::EditRole).toString().contains(filterData.filterId);
        }
    }

    return false;
}


